///////////////////////////////////////////////////////////////////////////////
//	FILENAME:	eTFileLink.m
//	SUMMARY:	Implementation of file components for eText documents
//	SUPERCLASS:	eTImage
//	INTERFACE:	None
//	PROTOCOLS:	<Annotation,HTMDSupport,ASCIISupport,LaTeXSupport,Tool,
//				InspectableTarget>
//	AUTHOR:		Rohit Khare
//	COPYRIGHT:	(c) 1994 California Institure of Technology, eText Project
///////////////////////////////////////////////////////////////////////////////
//	DESCRIPTION
//		Does its job by an eTComponent and execString.
///////////////////////////////////////////////////////////////////////////////
//	HISTORY
//	10/30/94:	Modified to support <InspectableTarget>
//	08/06/94:	Added symbolic linking support.
//	07/21/94:	Created. First actual implementation.
///////////////////////////////////////////////////////////////////////////////

#import "eTFileLink.h"
#define _eTFileLinkPR1		10	
#define _eTFileLinkVERSION	20

@implementation eTFileLink
//	id		etFileComponent;
//	char	*execString;
//	BOOL	isExecEnabled;
//	BOOL	useSymlink;

- setSymlink:(BOOL)newState
	{useSymlink = newState; return self;}
- setExecEnabled:(BOOL)newState
	{isExecEnabled = newState; return self;}
- (BOOL)useSymlink
	{return useSymlink;}
- (BOOL)isExecEnabled
	{return isExecEnabled;}
- (const char *)componentName
	{return ((etFileComponent) ? [etFileComponent componentName] : NXUniqueString(""));}
- (BOOL)isLinked
	{return ((etFileComponent) ? [etFileComponent isLinked] : NO);}
- (const char *)execString
	{return execString;}
- setExecString:(const char *) newString
{
	if (!newString) return self;
	if ((!(*execString)) && *newString)
		isExecEnabled = YES;
	execString = realloc(execString, (strlen(newString)+1)*sizeof(char));
	strcpy(execString, newString);
	return self;
}
////////////////////////////
+ toolAwake:theApp
{
	[theApp   registerAnnotation: [eTFileLink class] 
							name: "eTFileLink"
					RTFDirective: "eTFileLink"
					   menuLabel: NULL
						 menuKey: '\0'
						menuIcon: (NXImage *) nil];
	[theApp registerType:NXCreateFileContentsPboardType(ANY_TYPE) for:[eTFileLink class]];
	[eTRTFDFileLink toolAwake:theApp];	// "delegate chain"
	return self;
}

- init
{
	[super init];
	
	[self setUsesButtonStyle:NO];
	[self setDraggable:YES];
	isExecEnabled = NO;
	useSymlink = NO;
	if (!execString) {
		execString = malloc(2 * sizeof(char));
		execString[0] = '\0';
	}
	return self;
}

- free 
{
	free(execString);
	etFileComponent = [etFileComponent free];
	return self = [super free];
}

- _hack:sender
{
	// this is to prevent deadlock with the Workspace when
	// requesting an icon during a dragging session
	
	// This is hardcoded: all FileLinks are ANY_TYPE; icons are .tiff
	char imgName[MAXPATHLEN];
	
	sprintf(imgName,"%s.tiff",[etFileComponent componentName]);
	[self setImageComponent:[[[eTImageComponent alloc] initInDoc:etDoc linked:NO] useImage:[etFileComponent icon] name:imgName path:NXUniqueString("") shared:NO]];
	[self updateGraphics];
	return self;
}
- initFromPboard:thePB inDoc:theDoc linked:(BOOL) linked
{
	NXAtom 	types[3];
	char	*data;
	int		len;

	[self init];
	// we prevent super from executing initFromPboard, so we have to:
	etDoc = theDoc;
	theText = [[etDoc docUI] eTextObj]; // consistency checking
	[etDoc registerNotification:self];
	
	types[0] = NXCreateFileContentsPboardType(ANY_TYPE);
	types[1] = NXFilenamePboardType;
	types[2] = [thePB findAvailableTypeFrom:types num:2];

	if (!types[2]) return self;
	if (types[2] == NXFilenamePboardType) {
		[thePB readType:NXFilenamePboardType data:&data length:&len];
		if (index(data,'\t')) *(index(data,'\t'))=0;
		etFileComponent = [[eTComponent alloc] initInDoc:etDoc
							 linked:linked];
		[etFileComponent readComponentFromPath:data];
		[self perform:@selector(_hack:) with:nil afterDelay:0 cancelPrevious:NO];
		[thePB deallocatePasteboardData:data length:len];
	} else {
		NXLogError("%s got fcontents without fnamepboard! %s instead.\n",
					[[self class] name], types[2]);
	}
	return self;
}
- writeComponentToPath:(NXAtom)path inFormat:(int) theFormat
{
	if(!etDoc)	NXLogError("etDoc is nil at %s %u",__FILE__,__LINE__);
	if (useSymlink)
		[etFileComponent writeComponentToPath:path inFormat:theFormat];
	else
		[etFileComponent linkComponentToPath:path];
	return [super writeComponentToPath:path inFormat:theFormat];
}
- addToPboard:pboard
{
	[etFileComponent addToPboard:pboard];
	return self;
}
- readRichText:(NXStream *)stream forView:view 
{
	int i, ver;
	char buf[MAXPATHLEN];
	
	NXScanf(stream, "%d ", &ver);
	if ((ver != _eTFileLinkVERSION) && (ver != _eTFileLinkPR1)) {
		// bad version block.
		NXLogError("eTFileLink found unparseable version %d at position %d",
					ver, NXTell(stream));
		return nil;
	}

	NXScanf(stream, "%c ", &isExecEnabled);
	isExecEnabled -= 'A';

	if (ver == _eTFileLinkVERSION) {
		NXScanf(stream, "%c ", &useSymlink);
		useSymlink -= 'A';
	}
	
	NXScanf(stream, "%d", &i); NXGetc(stream); // space-eater
	if (i) {
		NXRead(stream, buf, i);
		buf[i] = 0;
		[self setExecString:buf];
		NXGetc(stream); // trailing space
	} // if i was zero, we have already advanced to the next non-white bit.
	[super readRichText:stream forView:view];
	NXGetc(stream); // separating space
	etFileComponent = [[[eTComponent alloc] init] readRichText:stream forView:view];
	return self;
}

- writeRichText:(NXStream *)stream forView:view
{	
	NXPrintf(stream, "%d %c %c %d %s ", _eTFileLinkVERSION,
		isExecEnabled + 'A', useSymlink + 'A', strlen(execString), execString); 
	[super writeRichText:stream forView:view];
	NXPutc(stream, ' ');	// separate the two visually
	[etFileComponent writeRichText:stream forView:view];
	return self;
}

- writeASCIIRef:(NXStream *)stream forView:view
{
	NXPrintf(stream, "See the file %y (last known address:%s)\n\t", [etFileComponent componentName], [etFileComponent currentPath]);
	[super writeASCIIRef:stream forView:view];
	return self;
}

- writeLaTeX:(NXStream*)stream forView:view
{	
	[super writeLaTeX:stream forView:view];
	[etFileComponent writeLaTeX:stream forView:view 
					 andClose:(*execString ? YES : NO)];
	if (*execString)
		NXPrintf(stream,
			" Try executing the file with {\\tt %w}.}\n", execString);
	return self;
}

- writeHTML:(NXStream *)stream forView:view
{ 		
	[etFileComponent writeHTML:stream forView:view andClose:NO];
	[super writeHTML:stream forView:view];	// icon-buttons.
	if (*execString)
		NXPrintf(stream,
			"(execute this file with <TT>&quot;%v&quot;</TT>)", execString);
	NXPrintf(stream,"</A>");
	return self;
}

- doubleClick:(NXEvent*)e
{	
	NXAtom fname = [etFileComponent currentPath];

	if (*fname && !access(fname, F_OK|R_OK)) {
		//ok, now do we open or use execString?
		if (isExecEnabled && *execString) {
			char cmd[MAXPATHLEN];
			id	sPanel;
			
			sprintf(cmd, execString, fname, fname, fname); 
					// undocumented hole: extra fnames are passed on the stack
			sPanel = NXGetAlertPanel("eTFileLink",
							"Executing the command \"%s\"",
							NULL, NULL, NULL, cmd);
			[sPanel setFloatingPanel:NO];
			[sPanel setHideOnDeactivate:YES];
			[sPanel orderFront:self];
			NXPing();
			system(cmd);
			if (sPanel) {
				[sPanel orderOut:self];
				NXFreeAlertPanel(sPanel);
			}
		} else {
			// we need to flip the location y coord
			NXPoint pt;
			NXSize	sz;
			id 		img;
			
			pt = e->location;
			img = [etFileComponent icon];
			[theText convertPoint:&pt fromView:nil];
			[img getSize:&sz];
			pt.x -= sz.width/2;
			pt.y += sz.height/2;
			[[Application workspace]
				openFile:fname fromImage: img
				at: &pt inView:theText];
		}
	} else {
		if (*fname) 
			NXRunAlertPanel("eTFileLink","Could not access() %s.","OK",NULL,NULL,fname);
	}
	return self; 
}

- inspect:(NXEvent *) e
{
	[[NXApp inspector] inspect:self];
	return self;
}
- (id <Inspectable>) inspectableDelegate {
	return [[eTFileLinkUI new] setAnnotation:self]; }

- drag: (Pasteboard *)draggingPboard image:(NXImage **)proxyImage
{
	if (! etFileComponent) {
		*proxyImage = nil;
		return nil;
	}
	[etFileComponent writeComponentToPboard:draggingPboard];
	*proxyImage = [etFileComponent icon];
	return self;
}
@end